use hypers::prelude::*;
use serde::{Deserialize, Serialize};
use hypers::tracing::info;

#[derive(Serialize, Deserialize, Default,Debug,PartialEq)]
pub struct User {
    pub email: Option<String>,
    pub age: Option<u16>,
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct HeaderParams {
    pub host: String,
    pub accept: String,
}

#[controller(name = "macro")]
impl User {
    // http://127.0.0.1:7878/macro/header
    // Request Header Params
    #[get("/header")]
    pub async fn header(host: Header<String>, accept: Header<String>) -> Result<(i32,Json<HeaderParams>)> {
        Ok((200, Json(HeaderParams{host: host.0,accept: accept.0})))
    }
    // http://127.0.0.1:7878/macro/admin@outlook.com/18
    // Uri Path Params
    #[delete("/:email/:age")]
    pub async fn delete(email: Path<String>, age: Path<u16>) -> impl Responder {
        (200, Json(User{email: Some(email.0),age: Some(age.0)}))
    }
    // http://127.0.0.1:7878/macro/query?email=admin@outlook.com&age=19
    // Query Params
    #[get("/query")]
    pub async fn query(email: Query<String>, age: Query<u16>) -> impl Responder {
        (200, Json(User{email: Some(email.0),age: Some(age.0)}))
    }
}

struct Api;
#[controller(name = "macro")]
impl Api {
    // http://127.0.0.1:7878/macro/create
    // Context-Type : application/json
    #[post("/create")]
    pub async fn create(user: Json<User>) -> impl Responder {
        Ok::<_, Error>((200, Json(user.0)))
    }
    // http://127.0.0.1:7878/macro/update
    // Context-Type : application/x-www-form-urlencoded
    #[put("/update")]
    async fn update(user: Form<User>) -> impl Responder {
        Ok::<_, Error>((200, user))
    }
    // http://127.0.0.1:7878/macro/multipart_form
    // Context-Type : multipart/form-data Form Fields
    #[post("multipart_form")]
    pub async fn multipart_form(user: Form<User>) -> impl Responder {
        Ok::<_, Error>((200, user))
    }
    // http://127.0.0.1:7878/macro/body_bytes
    // Context-Type : application/json
    #[post("/body_bytes")]
    pub async fn body_bytes(req: &mut Request) -> impl Responder {
        let bytes = req.payload().await?;
        let bytes = bytes.clone();
        Ok::<_, Error>((200, bytes))
    }
}

async fn start_server() -> Result<()> {
    let mut root = Router::default();
    root.push(User::default());
    root.push(Api);
    info!("root = {:#?}", root);
    let listener = hypers::TcpListener::bind("127.0.0.1:7878").await?;
    hypers::listen(root, listener).await
}

#[tokio::main]
async fn main() -> Result<()> {
    tracing_subscriber::fmt().init();
    start_server().await
}

#[cfg(test)]
mod test {
    use feignhttp::{get,delete,post,put,Result};
    use super::{User,HeaderParams,start_server};

    const BASE_URL: &str = "http://127.0.0.1:7878/macro";

    #[get(url = BASE_URL,path = "/header")]
    async fn header(#[header] head: &HeaderParams) -> Result<HeaderParams>{}

    #[delete(url = BASE_URL,path = "/{email}/{age}")]
    async fn delete(#[path] email: &str,#[path] age: u16) -> Result<User>{}

    #[get(url = BASE_URL,path = "/query")]
    async fn query(#[query] user: User) -> Result<User>{}

    #[post(url = BASE_URL,path = "/create")]
    async fn create(#[body] user: User) -> Result<User>{}

    #[put(url = BASE_URL,path = "/update")]
    async fn update(#[form] email: &str,#[form] age: u16) -> Result<String>{}

    #[post(url = BASE_URL,path = "/multipart_form")]
    async fn multipart_form(#[form] user: User) -> Result<String>{}

    #[post(url = BASE_URL,path = "/body_bytes")]
    async fn body_bytes(#[body] data: Vec<u8>) -> Result<Vec<u8>>{}

    #[tokio::test]
    async fn test() -> Result<()>{
        tokio::task::spawn(async {
            let _ = start_server().await;
        });
        tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
        
        let header_params = HeaderParams { 
            host: "127.0.0.1:7878".to_owned(), 
            accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7".to_owned()
        };
        assert_eq!(header(&header_params).await?,header_params);
        assert_eq!(delete("admin@outlook.com",18).await?,User{ email: Some("admin@outlook.com".to_owned()), age: Some(18) });
        assert_eq!(query(User{ email: Some("admin@outlook.com".to_owned()), age: Some(20) }).await?,User{ email: Some("admin@outlook.com".to_owned()), age: Some(20) });
        assert_eq!(create(User{ email: Some("admin@outlook.com".to_owned()), age: Some(21) }).await?,User{ email: Some("admin@outlook.com".to_owned()), age: Some(21) });
        assert_eq!(update("admin@outlook.com",22).await?,"email=admin%40outlook.com&age=22");
        assert_eq!(multipart_form(User{ email: Some("admin@outlook.com".to_owned()), age: Some(23) }).await?,"email=admin%40outlook.com&age=23");
        assert_eq!(body_bytes(vec![1,2,3]).await?,vec![1,2,3]);
        Ok(())
    }
}

